package com.xl.coalbum.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xl.coalbum.config.ElasticSearchConfig;
import com.xl.coalbum.constant.EsConstant;
import com.xl.coalbum.exception.BusinessException;
import com.xl.coalbum.exception.ErrorCode;
import com.xl.coalbum.exception.ThrowUtils;
import com.xl.coalbum.model.dto.picture.PictureQueryRequest;
import com.xl.coalbum.model.entity.Picture;
import com.xl.coalbum.model.entity.Space;
import com.xl.coalbum.model.entity.User;
import com.xl.coalbum.model.vo.PictureVO;
import com.xl.coalbum.model.vo.space.analyze.SpaceTagAnalyzeResponse;
import com.xl.coalbum.service.PictureSearchService;
import com.xl.coalbum.service.PictureService;
import com.xl.coalbum.service.SpaceService;
import com.xl.coalbum.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class PictureSearchServiceImpl implements PictureSearchService {
    @Resource
    private RestHighLevelClient esRestClient;
    @Resource
    private PictureService pictureService;
    @Resource
    private UserService userService;
    @Resource
    private SpaceService spaceService;

    @Override
    public Page<PictureVO> searchPicture(PictureQueryRequest pictureQueryRequest) throws IOException {
        // 限制爬虫
        ThrowUtils.throwIf(pictureQueryRequest.getPageSize() > 20, ErrorCode.PARAMS_ERROR);
        // === 构建查询 ===
        SearchRequest searchRequest = new SearchRequest(EsConstant.PICTURE_INDEX);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 1. 关键词搜索：name 和 introduction
        if(StrUtil.isNotBlank(pictureQueryRequest.getSearchText())){
            MultiMatchQueryBuilder multiMatchQuery = QueryBuilders.multiMatchQuery(pictureQueryRequest.getSearchText(), "name", "introduction")
                    .type(MultiMatchQueryBuilder.Type.BEST_FIELDS);
            boolQuery.must(multiMatchQuery);
        }
        // 2. 分类精确匹配
        if(StrUtil.isNotBlank(pictureQueryRequest.getCategory())){
            boolQuery.must(QueryBuilders.termQuery("category", pictureQueryRequest.getCategory()));
        }
        // 3. 编辑时间范围
        boolQuery.must(QueryBuilders.rangeQuery("editTime")
                .gte(pictureQueryRequest.getStartEditTime())
                .lte(pictureQueryRequest.getEndEditTime()));

        // 4. 标签 AND 逻辑：必须包含所有 mustTags 中的标签
        if(pictureQueryRequest.getTags()!=null){
            for (String tag : pictureQueryRequest.getTags()) {
                boolQuery.must(QueryBuilders.termQuery("tags.keyword", tag));
            }
        }
        //TODO 图片宽高条件筛选
        // 格式筛选

        Long spaceId = pictureQueryRequest.getSpaceId();
        if (spaceId == null) {
            // 公开图库
            // 普通用户默认只能看到审核通过的数据
            boolQuery.mustNot(QueryBuilders.existsQuery("spaceId"));
        } else {
            // 私有空间
            boolQuery.must(QueryBuilders.termQuery("spaceId",spaceId));
        }

        sourceBuilder.query(boolQuery);

        // 5. 分页
        int from = (pictureQueryRequest.getCurrent() - 1) * pictureQueryRequest.getPageSize();
        sourceBuilder.from(from);
        sourceBuilder.size(pictureQueryRequest.getPageSize());

        // 6. 排序（可选）
        if (StrUtil.isNotBlank(pictureQueryRequest.getSortField())){
            sourceBuilder.sort(pictureQueryRequest.getSortField(),
                    "descend".equals(pictureQueryRequest.getSortOrder())? SortOrder.DESC:SortOrder.ASC);
        }
        searchRequest.source(sourceBuilder);

        // 7. 执行查询
        SearchResponse response = esRestClient.search(searchRequest,ElasticSearchConfig.COMMON_OPTIONS);
        System.out.println(sourceBuilder.toString());

        // 8. 处理结果
        return buildSearchResult(response,pictureQueryRequest.getCurrent(),pictureQueryRequest.getPageSize());
    }

    private Page<PictureVO> buildSearchResult(SearchResponse searchResponse,int pageNum,int pageSize){
        // 1. 获取总记录数
        long totalHits = searchResponse.getHits().getTotalHits().value;

        // 2. 获取当前页的搜索结果（SearchHit 数组）
        SearchHits hits = searchResponse.getHits();
        SearchHit[] searchHits = hits.getHits();

        // 3. 将 SearchHit 转为 PictureVO 列表
        List<PictureVO> pictureVOList = new ArrayList<>();
        for (SearchHit hit : searchHits) {
            PictureVO pictureVO = JSON.parseObject(hit.getSourceAsString(), PictureVO.class);
            pictureVOList.add(pictureVO);
        }

        // 4. 构造 MyBatis-Plus 的 Page 对象
        Page<PictureVO> page = new Page<>(pageNum, pageSize, totalHits);
        page.setRecords(pictureVOList);

        return page;
    }

    @Override
    public boolean bulkSavePicturesToEs(List<PictureVO> pictures) throws IOException {
        //bulk 批量操作
        BulkRequest bulkRequest=new BulkRequest();
        for (PictureVO pictureVO : pictures) {
            //保存在哪个index下
            IndexRequest indexRequest = new IndexRequest(EsConstant.PICTURE_INDEX);
            //设置id
            indexRequest.id(pictureVO.getId().toString());
            //将检索信息 转为 json
            String picJson= JSON.toJSONString(pictureVO);
            //设置数据元 并指定 数据源格式
            indexRequest.source(picJson, XContentType.JSON);
            //将 索引请求 加入到 bulk中
            bulkRequest.add(indexRequest);
        }
        //执行 bulk
        BulkResponse bulkResponse = esRestClient.bulk(bulkRequest, ElasticSearchConfig.COMMON_OPTIONS);

        boolean hasFailure = bulkResponse.hasFailures();
        List<String> collect = Arrays.stream(bulkResponse.getItems()).map(item -> {
            return item.getId();
        }).collect(Collectors.toList());
        if(hasFailure){
            log.error("部分图片保存至es失败，失败Id:{}",collect);
        }
        return ! hasFailure;
    }
    @Override
    public boolean savePictureToEs(PictureVO pictureVO) throws IOException {
        return this.bulkSavePicturesToEs(Collections.singletonList(pictureVO));
    }

    @Override
    public boolean saveAllPassedPic() throws IOException {
        List<Picture> pictures = pictureService.list();
        //审核通过
        List<PictureVO> collect = pictures.stream()
                .filter(picture -> {return picture.getReviewStatus()==1;})
                .map(PictureVO::objToVo).collect(Collectors.toList());
        return this.bulkSavePicturesToEs(collect);
    }
    @Override
    public List<SpaceTagAnalyzeResponse> getTagList(int size,Long spaceId) throws IOException {
        SearchRequest searchRequest = new SearchRequest(EsConstant.PICTURE_INDEX);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        if(spaceId!=null){
            sourceBuilder.query(QueryBuilders.matchQuery("spaceId",spaceId));
        }else{
            //公共空间，查询不存在spaceId的doc
            sourceBuilder.query(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("spaceId")));
        }
        TermsAggregationBuilder allTags = AggregationBuilders.terms("all_tags").field("tags.keyword").size(size);
        sourceBuilder.aggregation(allTags);
        // 不返回 hits，只返回聚合
        sourceBuilder.size(0);
        searchRequest.source(sourceBuilder);
        //System.out.println("DSL:");
        //System.out.println(sourceBuilder.toString());
        // 执行查询
        SearchResponse response = esRestClient.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);
        // 解析聚合结果
        Terms terms = response.getAggregations().get("all_tags");
        List<SpaceTagAnalyzeResponse> collect = terms.getBuckets().stream().map(bucket -> {
            SpaceTagAnalyzeResponse tag = new SpaceTagAnalyzeResponse();
            tag.setTag(bucket.getKeyAsString());
            tag.setCount(bucket.getDocCount());
            return tag;
        }).collect(Collectors.toList());
        return collect;
    }


}
